﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Resources;
using System.ComponentModel.Design;
using System.Collections;

namespace InnovTech.Util.Resources
{
    /// <summary>
    /// This class describes a .NET resources file with the extension ".resx" and uses the class <see cref="ResxEntity"/> to manage all key-value pairs in that resource file.
    /// </summary>
    /// <remarks>
    /// At first one must call the method Load before GetXXX once.
    /// </remarks>
    public class ResxFile: Dictionary<string, ResxEntity>
    {
        #region ctor
        public ResxFile(string alias, string filePath)
        {
            Alias = alias;
            FilePath = filePath;
        }

        #endregion ctor

        #region fields

        public static string Extension = ".resx";

        private string _filePath;

        #endregion fields

        #region properties

        /// <summary>
        /// Gets or sets the file path of this resx file.
        /// </summary>
        public string FilePath
        {
            get
            {
                return _filePath;
            }
            protected set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("The file path for .NET resources can not be empty or null");
                if (!IsResxFile(value))
                    throw new ArgumentException("The given file is not a .NET resources file");
                _filePath = value;
                Language = GetCulture(value);
            }
        }
        /// <summary>
        /// Gets or sets the language of this resx file
        /// </summary>
        public CultureInfo Language
        {
            get;
            protected set;
        }

        /// <summary>
        /// Indicates whether this resx fils is a neutral language file.
        /// </summary>
        public bool IsNeutral
        {
            get
            {
                return Language == null;
            }
        }

        /// <summary>
        /// Gets or sets the alias of this resx file.
        /// </summary>
        public string Alias { get; set; }

        /// <summary>
        /// Gets the name of the related neutral resx file but without extension
        /// </summary>
        public string RelatedNeutralFileName
        {
            get
            {
                return GetNeutralResxFileName(FilePath);
            }
        }
        #endregion properties

        #region methods

        #region public
        /// <summary>
        /// Gets the value for the given key.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public object GetValue(string key)
        {
            if (ContainsKey(key))
                return this[key].Value;
            return null;
        }

        /// <summary>
        /// Gets the comment for the given key.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetComment(string key)
        {
            if (ContainsKey(key))
                return this[key].Comment;
            return null;
        }

        /// <summary>
        /// Sets the value and comment for the given key, or adds them if the key does not exist.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="comment"></param>
        public void SetValue(string key, object value, string comment = null)
        {
            ResxEntity re = null;
            if (ContainsKey(key))
                re = this[key];
            else
            {
                re = new ResxEntity() { Key = key };
                Add(key, re);
            }
            re.Value = value;
            if (comment != null)
                re.Comment = comment;
        }

        /// <summary>
        /// Saves all key-value paires to the given file using <see cref="System.Windows.Forms.ResXResourceWriter"/>.
        /// </summary>
        /// <param name="fileName"></param>
        public void Save(string fileName)
        {
            using (ResXResourceWriter writer = new ResXResourceWriter(fileName))
            {
                foreach (string key in Keys)
                {
                    ResXDataNode keyValueComment = new ResXDataNode(key, GetValue(key)) { Comment = GetComment(key) };
                    writer.AddResource(keyValueComment);
                }
                writer.Generate();
                writer.Close();
            }
        }

        /// <summary>
        /// Saves all key-value paires to the given file using <see cref="System.Windows.Forms.ResXResourceWriter"/>.
        /// </summary>
        public void Save()
        {
            Save(FilePath);
        }

        /// <summary>
        /// Loads the key-value pairs from a resource file. The path of the file must be given before.
        /// </summary>
        /// <param name="loadRefFiles"></param>
        public void Load(bool loadRefFiles = false)
        {
            Load(FilePath, loadRefFiles);
        }
        /// <summary>
        /// Loads the key-value paires from the given file. Whether the referenced files of the resources file would be load or not, it is decided by the second parameter. 
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="loadRefFiles"></param>
        public void Load(string filePath, bool loadRefFiles)
        {
            Clear();
            if (File.Exists(filePath))
            {
                using (ResXResourceReader rr = new ResXResourceReader(filePath))
                {
                    rr.UseResXDataNodes = true;
                    foreach (DictionaryEntry keyValue in rr)
                    {
                        ResXDataNode keyValueComment = keyValue.Value as ResXDataNode;
                        object value = null;
                        if (keyValueComment.FileRef != null)
                        {
                            if (!loadRefFiles)
                                continue;
                        }
                        else
                        {
                            value = keyValueComment.GetValue((ITypeResolutionService)null);
                        }

                        string comment = keyValueComment.Comment;
                        string key = keyValueComment.Name;
                        SetValue(key, value, comment);
                    }
                    rr.Close();
                }
            }
        }
        #endregion public

        #region static

        /// <summary>
        /// [Path]\[Resx_File_Name].[Language_Info].resx
        /// Language_Info: de-DE, en-EN
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static bool IsNeutralResxFile(ResxFile file)
        {
            return IsNeutralResxFileName(file.FilePath);
        }

        /// <summary>
        /// Indicates whether the given file is a resx file.
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool IsResxFile(string filePath)
        {
            return Extension.Equals(Path.GetExtension(filePath).ToLower());
        }

        /// <summary>
        /// Gets the language information of the given resx file.
        /// </summary>
        /// <param name="resxFilePath"></param>
        /// <returns></returns>
        public static CultureInfo GetCulture(string resxFilePath)
        {
            string fileName = Path.GetFileNameWithoutExtension(resxFilePath);
            int index = fileName.LastIndexOf('.');
            if (index < 0) return null; //no language information
            string lan = fileName.Substring(index + 1);

            try
            {
                CultureInfo culture = new CultureInfo(lan);
                return culture;
            }
            catch (CultureNotFoundException)
            {
                return null;
            }
        }

        /// <summary>
        /// Indicates whether the resx file with the given path is a neutral resx file.
        /// </summary>
        /// <param name="resxFilePath"></param>
        /// <returns></returns>
        public static bool IsNeutralResxFileName(string resxFilePath)
        {
            return GetCulture(resxFilePath) == null;
        }

        /// <summary>
        /// Gets the corresponding neutral language resx file for the resx file with the given path.
        /// </summary>
        /// <param name="resxFilePath"></param>
        /// <returns></returns>
        public static string GetNeutralResxFileName(string resxFilePath)
        {
            string fileName = Path.GetFileNameWithoutExtension(resxFilePath);
            int index = fileName.LastIndexOf('.');
            if (index < 0) return fileName; //no language information
            string lan = fileName.Substring(index + 1);
            try
            {
                CultureInfo culture = new CultureInfo(lan);
                return fileName.Substring(0, index);
            }
            catch (CultureNotFoundException)
            {
                return fileName;
            }
        }

        #endregion static 

        #endregion methods


    }
}
